// Package article
// Created by GoLand
// @User: lenora
// @Date: 2024/8/7
// @Time: 15:57

package article

import (
	"camp/microbook/internal/service"
	"camp/microbook/pkg/logger"
	"context"
	"github.com/gotomicro/redis-lock"
	"sync"
	"time"
)

type RankingJob struct {
	svc       service.RankingService
	client    *rlock.Client
	key       string
	timeout   time.Duration
	l         logger.Logger
	lock      *rlock.Lock
	localLock sync.Mutex
}

func NewRankingJob(svc service.RankingService, client *rlock.Client, l logger.Logger) *RankingJob {
	return &RankingJob{
		svc:     svc,
		client:  client,
		key:     "job:article_ranking",
		timeout: time.Second * 30,
		l:       l,
	}
}

func (j *RankingJob) Name() string {
	return "article_ranking"
}

func (j *RankingJob) Spec() string {
	return "@every 1s"
}

func (j *RankingJob) Run() error {
	j.localLock.Lock()
	lock := j.lock
	if lock == nil {
		var err error
		ctx, cancel := context.WithTimeout(context.Background(), time.Second*4)
		defer cancel()
		lock, err = j.client.Lock(ctx, j.key, j.timeout, &rlock.FixIntervalRetry{
			Interval: time.Millisecond * 100,
			Max:      3,
		}, time.Second)
		if err != nil {
			j.localLock.Unlock()
			return err
		}
		j.lock = lock
		j.localLock.Unlock()

		go func() {
			err := lock.AutoRefresh(time.Second*45, time.Minute)
			if err != nil {
				//续约失败,重置锁
				j.localLock.Lock()
				j.lock = nil
				j.localLock.Unlock()
			}
		}()
	} else {
		j.localLock.Unlock()
	}

	ctx, cancel := context.WithTimeout(context.Background(), j.timeout)
	defer cancel()
	//j.l.Info("job exe")
	_, err := j.svc.TopN(ctx)
	return err
}

// RunV1 抢到锁执行结束后释放锁
func (j *RankingJob) RunV1() error {
	ctx, cancel := context.WithTimeout(context.Background(), time.Second*4)
	defer cancel()
	lock, err := j.client.Lock(ctx, j.key, j.timeout, &rlock.FixIntervalRetry{
		Interval: time.Millisecond * 100,
		Max:      3,
	}, time.Second)
	if err != nil {
		return err
	}
	defer func() {
		ctx, cancel := context.WithTimeout(context.Background(), time.Second)
		defer cancel()
		rErr := lock.Unlock(ctx)
		if rErr != nil {
			j.l.Error("release job locker failed", logger.Error(rErr))
		}
	}()

	ctx, cancel = context.WithTimeout(context.Background(), j.timeout)
	defer cancel()
	_, err = j.svc.TopN(ctx)
	return err
}
